package com.freedy.dataStructure.stack;

/**
 * 中缀表达式计算器
 * @author Freedy
 * @date 2021/3/16 18:24
 */
public class Calculator {

    public Double calculate(String expression) {
        ArrStack<Double> numStack=new ArrStack<>(10);
        ArrStack<Character> opsStack=new ArrStack<>(10);
        double num1=0;
        double num2=0;
        char ops=0;
        double res=0;
        char ch=' ';//将每次扫描得到的char保存到ch
        StringBuilder builder=new StringBuilder();
        for (int index = 0; index < expression.length(); index++) {
            ch=expression.charAt(index);
            if (isOps(ch)){
                //检测到是运算符就将前面的数字放入数字栈(numStack)中
                numStack.push( Double.parseDouble(builder.toString()));
                builder=new StringBuilder();
                if (!opsStack.isEmpty()&&priority(ch)<=priority(opsStack.peak())){
                    num1=numStack.pop();
                    num2=numStack.pop();
                    ops=opsStack.pop();
                    res=cal(num1,num2,ops);
                    //把运算结果加入数栈
                    numStack.push(res);
                    opsStack.push(ch);
                }else {
                    opsStack.push(ch);
                }
            }else {
                builder.append(ch);
            }
        }
        //将最后一个数推入栈
        numStack.push( Double.parseDouble(builder.toString()));
        //当表达式扫描完毕，就顺序的从数栈和符号栈中pop出相应的数和符号，并运行
        while (!opsStack.isEmpty()){
            num1=numStack.pop();
            num2=numStack.pop();
            ops=opsStack.pop();
            res=cal(num1,num2,ops);
            //把运算结果加入数栈
            numStack.push(res);
        }
        return numStack.pop();
    }

    /**
     * 判断运算符的优先级,优先级用数字来表示
     * 数字越大优先级越高
     * @param ops
     * @return
     */
    private int priority(char ops) {
        //int和char可以相互转换，char的底层也是数字
        if (ops == '*' || ops == '/') {
            return 1;
        } else if (ops == '+' || ops == '-') {
            return 0;
        } else {
            return -1;
        }
    }

    /**
     * 判断是不是一个运算符
     * @param val
     * @return
     */
    private boolean isOps(char val){
        return val=='+'||val=='-'||val=='*'||val=='/';
    }

    private double cal(double num1,double num2,int ops){
        double res = 0;//存放计算结果
        switch (ops){
            case '+':
                res=num1+num2;
                break;
            case '-':
                res=num2-num1;
                break;
            case '*':
                res=num1*num2;
                break;
            case '/':
                res=num2/num1;
                break;
            default:
                throw new RuntimeException("计算错误");
        }
        return res;
    }

}
